using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.IO;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Xml.Linq;

namespace StrongConfigGenerator.Targets
{
	public class Generator : CommonGenerator.CommonGenerator
	{
		public class KeyType
		{
			public String Key
			{
				get;
				set;
			}
			public String Type
			{
				get;
				set;
			}
			public bool Group
			{
				get;
				set;
			}
			public String MethodName
			{
				get
				{
					if(Group)
						return "GetSectionGroup";
					else
						return "GetSection";
				}
			}
		}
		public override void WriteClass(System.IO.TextWriter writer)
		{
			var config = ConfigurationObject;

			var sections = config.Sections.Keys.OfType<string>().Select(key => new KeyType
			{
				Key = key,
				Group = false,
				Type = GetSectionType(config, key)
			});
			//config.C
			var nsDom = CreateCodeNamespace();
			var typeDom = nsDom.Types[0];
			var groups = config.SectionGroups.Keys.OfType<string>().Select(key => new KeyType
			{
				Key = key,
				Group = true,
				Type = GetSectionGroupType(config, key)
			});
			var allSections = sections.Union(groups);
			XDocument configDocument = XDocument.Load(config.FilePath);

			var localSections = configDocument.Root.Elements();

			allSections = allSections.Where(s => localSections.Any(l => l.Name == s.Key));
			CodeMemberField configurationField = new CodeMemberField(typeof(Configuration), "_Configuration")
			{
				Attributes = MemberAttributes.Static | MemberAttributes.Private,
				//ConfigurationManager.OpenExeConfiguration( ConfigurationUserLevel.None );
				InitExpression = new CodeMethodInvokeExpression
					(
						new CodeTypeReferenceExpression(typeof(ConfigurationManager)),
						"OpenExeConfiguration",
						new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(ConfigurationUserLevel)), "None")
					)
			};


			typeDom.Members.Add(configurationField);
			foreach(var section in allSections.Where(s => s.Type != null))
			{
				CodeMemberProperty property = new CodeMemberProperty();
				property.HasSet = false;
				property.HasGet = true;
				property.Attributes = MemberAttributes.Static | MemberAttributes.Public;
				property.Type = new CodeTypeReference(section.Type);
				property.Name = Capitalize(section.Key.Replace(".", " ")).Replace(" ", ""); //system.web to SystemWeb

				var getSection = new CodeCastExpression(property.Type, new CodeMethodInvokeExpression
					(
						new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeDom.Name), "_Configuration"),
						section.MethodName,
						new CodePrimitiveExpression(section.Key)
					));
				property.GetStatements.Add(new CodeMethodReturnStatement(getSection));
				typeDom.Members.Add(property);
			}

			var provider = CreateProvider();
			provider.GenerateCodeFromNamespace(nsDom, writer, new CodeGeneratorOptions());
		}

		private string GetSectionType(Configuration conf, string key)
		{
			try
			{
				var section = conf.Sections[key].GetType().FullName;
				if(!section.GetType().IsPublic)
					return null;
				return section;
			}
			catch(FileNotFoundException ex)
			{
				var typeLoad = (TypeLoadException)ex.InnerException;
				return typeLoad.TypeName;
			}
			catch(ConfigurationErrorsException ex)
			{
				var typeLoad = (TypeLoadException)ex.InnerException;
				return typeLoad.TypeName;
			}
		}

		public static string Capitalize(string value)
		{
			return System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(value);
		}

		private string GetSectionGroupType(Configuration conf, string key)
		{
			try
			{
				return conf.SectionGroups[key].GetType().FullName;
			}
			catch(FileNotFoundException ex)
			{
				var typeLoad = (TypeLoadException)ex.InnerException;
				return typeLoad.TypeName;
			}
			catch(ConfigurationErrorsException ex)
			{
				var typeLoad = (TypeLoadException)ex.InnerException;
				return typeLoad.TypeName;
			}
		}
	}
}